#include <assert.h>
#include <vector>
#include <string>
#include <map>

#include "dbapi.h"
#include "dbapi_provider.h"
#include "sqlite3.h"

class CDbClient_Sqlite : public CDbClient {
public:
	CDbClient_Sqlite(const char* pDBFileName) {
		int ret;
		ret = sqlite3_open(pDBFileName, &db);
		if(ret!=SQLITE_OK) db = NULL;
	}
	virtual ~CDbClient_Sqlite() {
		if(db) sqlite3_close(db);
	}

	virtual void Release() {
		delete this;
	}
	virtual bool IsAvaliable() {
		return db!=NULL;
	}
	virtual bool EscapeString(char* pTo, const char* pFrom, size_t nLength) {
		sqlite3_snprintf(16384, pTo, "%q", pFrom);
		return true;
	}

	virtual bool BeginTrans() {
		int ret;
		char* errmsg;
		ret = sqlite3_exec(db, "begin transaction", NULL, NULL, &errmsg);
		if(ret!=SQLITE_OK) {
			SetError(ret, errmsg);
			return false;
		}
		return true;
	}
	virtual bool CommitTrans() {
		int ret;
		char* errmsg;
		ret = sqlite3_exec(db, "commit transaction", NULL, NULL, &errmsg);
		if(ret!=SQLITE_OK) {
			SetError(ret, errmsg);
			return false;
		}
		return true;
	}
	virtual bool RollbackTrans() {
		int ret;
		char* errmsg;
		ret = sqlite3_exec(db, "rollback transaction", NULL, NULL, &errmsg);
		if(ret!=SQLITE_OK) { SetError(ret, errmsg); return false; }
		return true;
	}

	virtual IDbRecordSet* Query(const char* Sql, int nMax) {
		int ret;
		sqlite3_stmt *stmt;
		int row_idx, col_count, col_idx;
		const char* next;

		ret = sqlite3_prepare(db, Sql, -1, &stmt, &next);
		if(ret!=SQLITE_OK) { SetError(ret,"unknown error"); return NULL; }

		col_count = sqlite3_column_count(stmt);
		CDbRecordSet* pRS = CDbRecordSet::New(sqlite3_column_count(stmt), nMax);
		if(pRS==NULL) { SetError(-1, "memory"); return NULL; }

		for(col_idx=0; col_idx<col_count; col_idx++) {
			pRS->SetFieldName(col_idx, sqlite3_column_name(stmt, col_idx));
		}

		for(row_idx=0; row_idx<nMax; row_idx++) {
			ret = sqlite3_step(stmt);
			if(ret!=SQLITE_ROW) {
				if(ret!=SQLITE_DONE) {
					sqlite3_finalize(stmt);
					SetError(ret, "unknown error");
					return NULL;
				}
				break;
			}

			for(col_idx=0; col_idx<col_count; col_idx++) {
				switch(sqlite3_column_type(stmt, col_idx)) {
				case SQLITE_INTEGER:
				case SQLITE_FLOAT:
				case SQLITE_TEXT:
				case SQLITE_BLOB:
					pRS->SetFieldValue(row_idx, col_idx, (char*)sqlite3_column_text(stmt, col_idx));
					break;
				case SQLITE_NULL:
					break;
				default:
					break;
				}
			}
		}
		sqlite3_finalize(stmt);

		if(row_idx==0) {
			SetError(IDbClient::DBERR_NOT_FOUND, "not found");
			pRS->Release();
			return NULL;
		}

		return pRS;
	}

	virtual bool Execute(const char* Sql) {
		int ret;
		char* errmsg;
		ret = sqlite3_exec(db, Sql, NULL, NULL, &errmsg);
		if(ret!=0) { SetError(ret, errmsg); return false; }
		return true;
	}

private:
	sqlite3*			db;
};

class CDbProvider_Sqlite : public CDbProvider {
public:
	virtual const char* GetName() {
		return "sqlite";
	}
	virtual void Init() {
	}
	virtual void Final() {
	}
	virtual CDbClient* CreateClient(const char* pConnStr) {
		std::map<std::string, std::string> Map;
		CrackConnString(pConnStr, Map);
		std::map<std::string, std::string>::iterator i;
		i = Map.find("dbfile");
		if(i==Map.end()) return NULL;
		return new CDbClient_Sqlite(i->second.c_str());
	}

protected:
	CDbProvider* m_pNext;
};
static CDbProvider_Sqlite dbprovider_sqlite;
void * _dbprovider_sqlite = &dbprovider_sqlite;
